// (C) 2022 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

module aes256_gcm_csr (
  input  logic clk,                 // system clock
  input  logic reset_n,             // system reset

  input  logic         aes_busy,
  input  logic         multH_generated,
  input  logic         auth_tag_valid,
  input  logic         lastround,
  input  logic         aes_flush_done,
  input  logic [127:0] aes_data_out,
  input  logic [127:0] aes_auth_tag,
  output logic [127:0] aes_data_in,
  output logic [127:0] aes_iv_in,
  output logic [255:0] aes_mask_seed,
  output logic [255:0] aes_key_in,

  input  logic         avm_write,
  input  logic [31:0]  avm_writedata,
  input  logic [7:0]   avm_address,
  output logic         avm_waitrequest,
  input  logic         avm_read,
  output logic [31:0]  avm_readdata,
  output logic         avm_readdatavalid,

  output reg  aes_flush,
  output reg  aes_start,
  output reg  aes_last,
  output reg  aes_op,
  output wire aes_data_in_valid,
  output wire aes_mask_seed_valid,
  output wire aes_key_in_valid,
  output wire aes_iv_in_valid,
  output reg  aes_ctrl_reg_valid
);

  localparam CSR_STATUS        = 7'h00;
  localparam CSR_CONTROL       = 7'h10;
  localparam CSR_AES_DATA_IN   = 7'h20;
  localparam CSR_AES_MASK_SEED = 7'h30;
  localparam CSR_AES_KEY_IN    = 7'h40;
  localparam CSR_AES_IV_IN     = 7'h50;
  localparam CSR_AES_DATA_OUT  = 7'h60;
  localparam CSR_AUTH_TAG_OUT  = 7'h70;

  localparam AES_KEY_LENGTH       = 256;
  localparam AES_MASK_SEED_LENGTH = 256;
  localparam AES_DATA_LENGTH      = 128;
  localparam AES_IV_LENGTH        = 128;
  localparam AES_AUTH_TAG_LENGTH  = 128;


  logic aes_data_in_valid_reg;
  logic aes_mask_seed_valid_reg;
  logic aes_key_in_valid_reg;
  logic aes_iv_in_valid_reg;

  logic [AES_DATA_LENGTH-1:0] aes_data_out_buf;
  logic [2:0] aes_key_in_cnt, aes_mask_seed_cnt;
  logic [1:0] aes_data_in_cnt, aes_iv_in_cnt;

  logic [31:0] aes_key_in_mem [0:7];

  logic [31:0]  avm_readdata_buf, aes_status_reg;
  logic avm_readdatavalid_reg;
  logic         aes_data_out_valid;

 //--Control reg---------------------------------------------
 always @(posedge clk or negedge reset_n)
 begin
   if (!reset_n)
   begin
     aes_flush <= 1'b0;
     aes_start <= 1'b0;
     aes_last  <= 1'b0;
     aes_op    <= 1'b0;
     aes_ctrl_reg_valid <= 1'b0;
   end
   else
   begin
     if (aes_flush_done)
     begin
       aes_flush <= 1'b0;
       aes_start <= 1'b0;
       aes_last  <= 1'b0;
       aes_op    <= 1'b0;
       aes_ctrl_reg_valid <= 1'b0;
     end
     else
     begin
       if (avm_write && avm_address[6:4] == CSR_CONTROL[6:4])
       begin
         aes_flush <= avm_writedata[0];
         aes_start <= avm_writedata[1];
         aes_last  <= avm_writedata[2];
         aes_op    <= avm_writedata[3];
         aes_ctrl_reg_valid <= 1'b1;
       end
       else
       begin
         aes_ctrl_reg_valid <= 1'b0;
       end
     end
   end
 end
 //----------------------------------------------------------

 //--AES Data IN reg ----------------------------------------
 assign aes_data_in_valid = aes_data_in_valid_reg;
 always @(posedge clk or negedge reset_n)
 begin
   if (~reset_n)
   begin
     aes_data_in           <= {AES_DATA_LENGTH{1'b0}};
     aes_data_in_valid_reg <= 1'b0;
     aes_data_in_cnt       <= 2'b00;
   end
   else
   begin
     if (aes_flush_done)
     begin
       aes_data_in           <= {AES_DATA_LENGTH{1'b0}};
       aes_data_in_valid_reg <= 1'b0;
       aes_data_in_cnt       <= 2'b00;
     end
     else
     begin
       if (avm_write && avm_address[6:4] == CSR_AES_DATA_IN[6:4])
       begin
         //--Implementation using shift reg n counter ----//
         aes_data_in_cnt <= aes_data_in_cnt + 1;
         if ((&aes_data_in_cnt))
         begin
           aes_data_in_valid_reg <= 1'b1;
         end
         else
         begin
           aes_data_in_valid_reg <= 1'b0;
         end
         aes_data_in[(AES_DATA_LENGTH-1) : (AES_DATA_LENGTH-32)] <= avm_writedata[31:0];
         aes_data_in[((AES_DATA_LENGTH-32)-1) : 0]               <= aes_data_in[(AES_DATA_LENGTH-1): 32];
         //--------------------------------------------------//
       end
       else
       begin
         aes_data_in_valid_reg <= 1'b0;
       end
     end
   end
 end
  // Write small FSM of 2 states if aes_busy=1 & aes_data_in_valid_reg=1.
  // Akshay to check if we need edge detection logic for aes_data_in_valid_reg
 //----------------------------------------------------------

 //--AES_MASK_SEED---------------------------------------
 assign aes_mask_seed_valid = aes_mask_seed_valid_reg;
 always @(posedge clk or negedge reset_n)
 begin
   if (~reset_n)
   begin
     aes_mask_seed           <= {AES_MASK_SEED_LENGTH{1'b0}};
     aes_mask_seed_valid_reg <= 1'b0;
     aes_mask_seed_cnt       <= 3'b000;
   end
   else
   begin
     if (avm_write && avm_address[6:4] == CSR_AES_MASK_SEED[6:4])
     begin
       //--Implementation using shift reg n counter ----//
       aes_mask_seed_cnt <= aes_mask_seed_cnt + 1;
       if ((&aes_mask_seed_cnt))
       begin
         aes_mask_seed_valid_reg <= 1'b1;
       end
       else
       begin
         aes_mask_seed_valid_reg <= 1'b0;
       end
       aes_mask_seed[(AES_MASK_SEED_LENGTH-1) : (AES_MASK_SEED_LENGTH-32)] <= avm_writedata[31:0];
       aes_mask_seed[((AES_MASK_SEED_LENGTH-32)-1) : 0]                    <= aes_mask_seed[(AES_MASK_SEED_LENGTH-1): 32];
       //--------------------------------------------------//
     end
     else
     begin
       aes_mask_seed_valid_reg <= 1'b0;
     end
   end
 end
 //----------------------------------------------------------

 //--AES_KEY_IN----------------------------------------------
 assign aes_key_in_valid = aes_key_in_valid_reg;

 always @(posedge clk or negedge reset_n)
 begin
   if (~reset_n)
   begin
     aes_key_in           <= {AES_KEY_LENGTH{1'b0}};
     aes_key_in_valid_reg <= 1'b0;
     aes_key_in_cnt       <= 3'b000;
   end
   else
   begin
     if (avm_write && avm_address[6:4] == CSR_AES_KEY_IN[6:4])
     begin

       //--Implementation using shift reg n counter ----//
       aes_key_in_cnt <= aes_key_in_cnt + 1;
       if ((&aes_key_in_cnt))
       begin
         aes_key_in_valid_reg <= 1'b1;
       end
       else
       begin
         aes_key_in_valid_reg <= 1'b0;
       end
       aes_key_in[(AES_KEY_LENGTH-1) : (AES_KEY_LENGTH-32)] <= avm_writedata[31:0];
       aes_key_in[((AES_KEY_LENGTH-32)-1) : 0]              <= aes_key_in[(AES_KEY_LENGTH-1): 32];
       //--------------------------------------------------//
     end
     else
     begin
       aes_key_in_valid_reg <= 1'b0;
     end
   end
 end
 //----------------------------------------------------------

 //--AES_IV_IN-----------------------------------------------
 assign aes_iv_in_valid = aes_iv_in_valid_reg;

 always @(posedge clk or negedge reset_n)
 begin
   if (~reset_n)
   begin
     aes_iv_in           <= {AES_IV_LENGTH{1'b0}};
     aes_iv_in_valid_reg <= 1'b0;
     aes_iv_in_cnt       <= 2'b00;
   end
   else
   begin
     if (avm_write && avm_address[6:4] == CSR_AES_IV_IN[6:4])
     begin
       //--Implementation using shift reg n counter ----//
       aes_iv_in_cnt <= aes_iv_in_cnt + 1;
       if ((&aes_iv_in_cnt))
       begin
         aes_iv_in_valid_reg <= 1'b1;
       end
       else
       begin
         aes_iv_in_valid_reg <= 1'b0;
       end
       aes_iv_in[(AES_IV_LENGTH-1) : (AES_IV_LENGTH-32)] <= avm_writedata[31:0];
       aes_iv_in[((AES_IV_LENGTH-32)-1) : 0]             <= aes_iv_in[(AES_IV_LENGTH-1): 32];
       //--------------------------------------------------//
     end
     else
     begin
       aes_iv_in_valid_reg <= 1'b0;
     end
   end
 end
 //----------------------------------------------------------
 //--avm_read------------------------------------------------
  always @(posedge clk or negedge reset_n)
  begin
    if (~reset_n)
    begin
      aes_data_out_buf   <= {AES_DATA_LENGTH{1'b0}};
      aes_data_out_valid <= 1'b0;
    end
    else
    begin
      if (lastround == 1'b1)
      begin
        aes_data_out_buf <= aes_data_out;
        aes_data_out_valid <= 1'b1;
      end
      else if (avm_read && avm_address[6:4] == CSR_AES_DATA_OUT[6:4])
      begin
        aes_data_out_valid <= 1'b0;
      end
    end
  end

  assign aes_status_reg = {{28{1'b0}}, multH_generated, auth_tag_valid, aes_data_out_valid, aes_busy};
  always_ff @(posedge clk or negedge reset_n) begin
    if (~reset_n)
    begin
      avm_readdata_buf      <= 32'h0000_0000;
      avm_readdatavalid_reg <= 1'b0;
    end
    else
    begin
      if (avm_read) 
      begin
        avm_readdatavalid_reg  <= 1'b1;
        if (avm_address[6:0] == CSR_STATUS[6:0]) 
        begin
          avm_readdata_buf       <= aes_status_reg;
        end 
        else if (avm_address[6:2] == CSR_AES_DATA_OUT[6:2])
        begin
          avm_readdata_buf[31:0] <= aes_data_out_buf[32*avm_address[1:0] +: 32];
        end 
        else if (avm_address[6:2] == CSR_AUTH_TAG_OUT[6:2])
        begin
          avm_readdata_buf[31:0] <= aes_auth_tag[32*avm_address[1:0] +: 32];
        end
        else
        begin
          avm_readdata_buf[31:0] <= 32'h0000_0000;
        end
      end
      else
      begin
        avm_readdatavalid_reg  <= 1'b0;
      end
    end
  end
    
  assign avm_readdata      = avm_readdata_buf;  
  assign avm_readdatavalid = avm_readdatavalid_reg;
 //----------------------------------------------------------
  assign avm_waitrequest = 1'b0;

endmodule
